`timescale 1ns/100ps
`default_nettype none
/*************************************************
8 by 8 matrix calculation by using S' * C to get T
**************************************************/


module TCalc(
	input logic CLOCK_50_I,
	input logic resetn,
	input logic clearResult,						//Clear all the previous results
	output logic [6:0] DP0_Addr0,
	output logic [6:0] DP0_Addr1,
	input logic [31:0] DP0_Read0,
	input logic [31:0] DP0_Read1,
	
	output logic [6:0] DP1_Addr0,
	output logic [31:0] DP1_Write0,
	output logic DP1_we0,

    // Multiplier
    input logic [31:0] mulResult,           // Result of multiplier
    output logic [31:0] mulOp1,             // Op1 to multiplier
    output logic [31:0] mulOp2,             // Op2 to multiplier
	
	input logic start,
	output logic finish
);

enum logic [4:0]{
	S_IDLE,
	S0,
	S1,
	S2,
	S3,
	S4,
	S5,
	S6,
	S7,
	S8,
	S9,
	S9_WAIT,
	S10,
	S11
}state;
logic [8:0] count;  // 9 bits count use to track the address for multiplication
logic [8:0] count_buf;
logic [31:0] acc;   // Accumulator
logic [6:0] offset; // For bottom half Dual_Port RAM address tracking
assign offset = 7'd64;

always_ff @ (posedge CLOCK_50_I or negedge resetn) begin
    if (resetn == 1'b0) begin
        acc <= 32'd0;
        count <= 9'd0;
        count_buf <= 9'd0;
        DP0_Addr0 <= 7'd0;
        DP0_Addr1 <= offset;
		DP1_Addr0 <= 7'd0;
		DP1_Write0 <= 32'd0;
        DP1_we0 <= 1'b0;
        finish <= 1'b0;
        state <= S_IDLE;
    end else begin
        case (state)
		S_IDLE: begin
			if (start) begin//S'0 C0
				acc <= 32'd0;
				count <= 9'd0;
				count_buf <= 9'd0;
				DP0_Addr0 <= 7'd0;
				DP0_Addr1 <= offset;
				DP1_Addr0 <= 7'd0;
				DP1_Write0 <= 32'd0;
				DP1_we0 <= 1'b0;
                finish <= 1'b0;
				state <= S0;
            end else if (clearResult) begin
				acc <= 32'd0;
                count <= 9'd0;
                count_buf <= 9'd0;
                DP0_Addr0 <= 7'd0;
                DP0_Addr1 <= offset;
				DP1_Addr0 <= 7'd0;
				DP1_Write0 <= 32'd0;
                DP1_we0 <= 1'b0;
                finish <= 1'b0;
			end
        end
		S0: begin
			count <= count + 9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
            acc <= mulResult;
            finish <= 1'b0;
			DP1_we0 <= 1'b0;
			state <= S1;
		end
        S1: begin//S'1 C8
            count <= count +9'd1;				
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {count[2:0],count[5:3]};
            state <= S2;
        end
        S2: begin//S'2 C16
            acc <= acc + mulResult;//accumulate the result calculated by Mul_32
			count <= count + 9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
			DP1_we0 <= 1'b0;
            state <= S3;
        end
		S3: begin//S'3 C24
            acc <= acc + mulResult; 
			count <= count + 9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <=offset +  {1'b0, count[2:0],count[5:3]};
			state <= S4;
        end
		S4: begin//S'4 C32
            acc <= acc + mulResult; 
			count <= count + 9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
            state <= S5;
        end
		S5: begin//S'5 C40
            acc <= acc + mulResult; 
			count <= count + 9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
            state <= S6;
        end
		S6: begin//S'6 C48
            acc <= acc + mulResult; 
			count <= count + 9'd1;
			count_buf <= count + 9'd1;//buf count, for number of Row*Col calculation 
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
            state <= S7;
        end
        S7: begin//S'7 C56
            acc <= acc + mulResult;
			count <= count +9'd1;
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
			state <= S8;
        end
		S8: begin
			acc <= acc + mulResult;
			state <= S9;
		end
		S9: begin//start to read the first value of S' and C for the next loop
			acc <= acc + mulResult;
			count <= count + 9'd1;				
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
			state <= S9_WAIT;
		end
		S9_WAIT: begin//start to read the second value of S' and C for the next loop
			count <= count + 9'd1;	
			DP0_Addr0 <= {1'b0, count[8:6],count[2:0]};
			DP0_Addr1 <= offset + {1'b0, count[2:0],count[5:3]};
			//start to load the calculated T/256 into DP_RAM1_top part
			DP1_Addr0 <= {1'b0,count_buf[8:3]};
			DP1_Write0 <= {{8{acc[31]}},acc[31:8]};//sign extention
			DP1_we0 <= 1'b1;
			acc <= 32'd0;
			if (count_buf == 9'd511) begin//means one 8*8 matrix 511times calculation is done 
				DP1_Addr0 <= {1'b0,count_buf[8:3]};//start to load the last T/256 into DP_RAM1_top part
				DP1_Write0 <= {{8{acc[31]}},acc[31:8]};//sign extention
				DP1_we0 <= 1'b1;
				state <= S10;
			end
			else state <= S2;
		end
		S10: begin//lead out state,provide 2 clk cycles to make the result stable
			DP1_we0 <= 1'b0;
			state <= S11;
		end
		S11: begin
			finish <= 1'b1;
			acc <= 32'd0;
			count <= 9'd0;
			state <= S_IDLE;
		end
        default: state <= S_IDLE;
        endcase
    end
end

/********************************
 * Multiplication block
 ********************************/
always_comb begin
    case(state)
	S_IDLE: begin
		mulOp1 = 32'd0;
        mulOp2 = 32'd0;
	end
	S0: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    S1: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    S2: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
    S3: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
	S4: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
    S5: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
    S6: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
    S7: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
	S8: begin
		mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
	end
	S9: begin
        mulOp1 = DP0_Read0;
        mulOp2 = DP0_Read1;
    end
	S9_WAIT: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
	S10: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
	S11: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    default: begin
        mulOp1 = 32'd0;
        mulOp2 = 32'd0;
    end
    endcase
end

endmodule

